package com.duoec.magichour.invoker.impl;

import com.alibaba.fastjson.JSONObject;
import com.duoec.magichour.core.CommonConst;
import com.duoec.magichour.core.exceptions.BusinessException;
import com.duoec.magichour.core.utils.MobileUtils;
import com.duoec.magichour.dto.BaseResponse;
import com.duoec.magichour.enums.SmsMessageTypeEnum;
import com.duoec.magichour.invoker.SmsInvoker;
import com.duoec.magichour.pojo.Sms;
import com.google.common.base.Joiner;
import com.google.common.base.Strings;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import com.jayway.jsonpath.JsonPath;
import com.jayway.jsonpath.PathNotFoundException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;

import javax.crypto.Mac;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.text.SimpleDateFormat;
import java.util.*;

/**
 * 官方文档地址：https://api.alidayu.com/doc2/apiDetail?apiId=25450
 * 可用模板：
 * 模板ID: SMS_76415017
 * 模板内容: 美时美刻提醒您，手机验证码：${code}．
 * <p>
 * 模板ID: SMS_76505012
 * 模板内容: 美时美刻提醒您，账号初始密码：${pwd}．
 */
@Service
public class SmsInvokerAlidayuImpl extends BaseInvoker implements SmsInvoker {
    private static final Logger logger = LoggerFactory.getLogger(SmsInvokerAlidayuImpl.class);
    private static final String TIME_FORMAT_PATTERN = "yyyy-MM-dd HH:mm:ss";
    private static final String SMS_SEND_ERROR = "发送短信失败！";
    private static final String MOBILE_INVALID = "手机号码为空！";

    @Value("${sms.dayu.appKey}")
    private String appKey;

    @Value("${sms.dayu.appSecret}")
    private String appSecret;

    @Value("${sms.dayu.sendUrl}")
    private String sendUrl;

    @Value("${sms.dayu.signName}")
    private String signName;

    @Override
    public void send(Sms sms) {
        String templateCode;
        SmsMessageTypeEnum smsMessageType = SmsMessageTypeEnum.getByCode(sms.getMessageType());

        Map<String, String> params = Maps.newHashMap();
        switch (smsMessageType) {
            case MOBILE_CODE:
                //用户注册
                templateCode = "SMS_76415017";
                params.put("code", sms.getCode());
                break;
            case RESET_PWD:
                //找回密码，初始化手机密码
                templateCode = "SMS_76505012";
                params.put("code", sms.getCode());
                break;
            default:
                throw new BusinessException(BaseResponse.CODE_PARAM_INVALID, "暂不支持此类型短信！");
        }

        send(Lists.newArrayList(sms.getMobile()), templateCode, params);
    }

    /**
     * 发送短信
     *
     * @param mobileList     手机号码列表
     * @param templateCode   模板编号
     * @param templateParams 参数
     * @return
     */
    @Override
    public void send(List<String> mobileList, String templateCode, Map<String, String> templateParams) {
        String paramStr = JSONObject.toJSONString(templateParams);
        if (mobileList == null || mobileList.isEmpty()) {
            throw new BusinessException(BaseResponse.CODE_PARAM_INVALID, MOBILE_INVALID);
        }
        Set<String> mobiles = Sets.newHashSet();

        mobileList
                .stream()
                .filter(MobileUtils::isAvailabeMobile)
                .forEach(mobiles::add);

        if (mobiles.isEmpty()) {
            throw new BusinessException(BaseResponse.CODE_PARAM_INVALID, MOBILE_INVALID);
        }

        String mobileStr = Joiner
                .on(",")
                .skipNulls()
                .join(mobiles);

        Map<String, String> params = getParams();
        params.put("sms_type", "normal");
        params.put("sms_free_sign_name", signName);
        params.put("sms_param", paramStr);
        params.put("rec_num", mobileStr);
        params.put("sms_template_code", templateCode);

        String sign;
        try {
            sign = signTopRequest(params, appSecret);
        } catch (IOException e) {
            logger.error("计算签名出错！", e);
            throw new BusinessException(BaseResponse.CODE_PARAM_INVALID, SMS_SEND_ERROR);
        }
        params.put("sign", sign);

        ResponseEntity<String> rsp = post(sendUrl, params, null, null, String.class);
        if (rsp == null || rsp.getStatusCode() != HttpStatus.OK) {
            throw new BusinessException(BaseResponse.CODE_PARAM_INVALID, SMS_SEND_ERROR);
        }
        //{"alibaba_aliqin_fc_sms_num_send_response":{"result":{"err_code":"0","model":"108614550803^1111599272452","msg":"*","success":true},"request_id":"2mskamf8dv4h"}}
        String body = rsp.getBody();
        Boolean success;
        try {
            success = JsonPath.read(body, "$.alibaba_aliqin_fc_sms_num_send_response.result.success");
        } catch (PathNotFoundException e) {
            logger.error(e.getMessage(), e);
            throw new BusinessException(BaseResponse.CODE_PARAM_INVALID, SMS_SEND_ERROR);
        }
        if (!success) {
            logger.error("短信发送失败：{}", body);
            throw new BusinessException(BaseResponse.CODE_PARAM_INVALID, SMS_SEND_ERROR);
        }
    }

    private Map<String, String> getParams() {
        SimpleDateFormat sdf = new SimpleDateFormat(TIME_FORMAT_PATTERN);
        String timestamp = sdf.format(new Date());

        Map<String, String> params = Maps.newHashMap();
        params.put("app_key", appKey);
        params.put("method", "alibaba.aliqin.fc.sms.num.send");
        params.put("sign_method", "hmac");
        params.put("timestamp", timestamp);
        params.put("format", "json");
        params.put("v", "2.0");
        return params;
    }

    private String signTopRequest(Map<String, String> params, String secret) throws IOException {
        // 第一步：检查参数是否已经排序
        String[] keys = params.keySet().toArray(new String[0]);
        Arrays.sort(keys);

        // 第二步：把所有参数名和参数值串在一起
        StringBuilder query = new StringBuilder();

        for (String key : keys) {
            String value = params.get(key);
            if (!Strings.isNullOrEmpty(key) && !Strings.isNullOrEmpty(value)) {
                query.append(key).append(value);
            }
        }

        // 第三步：使用HMAC加密
        byte[] bytes = encryptHMAC(query.toString(), secret);

        // 第四步：把二进制转化为大写的十六进制
        return byte2hex(bytes);
    }

    private byte[] encryptHMAC(String data, String secret) throws IOException {
        byte[] bytes;
        try {
            SecretKey secretKey = new SecretKeySpec(secret.getBytes(CommonConst.ENCODE), "HmacMD5");
            Mac mac = Mac.getInstance(secretKey.getAlgorithm());
            mac.init(secretKey);
            bytes = mac.doFinal(data.getBytes(CommonConst.ENCODE));
        } catch (GeneralSecurityException e) {
            logger.error(e.getMessage(), e);
            throw new BusinessException(BaseResponse.CODE_PARAM_INVALID, SMS_SEND_ERROR);
        }
        return bytes;
    }

    private String byte2hex(byte[] bytes) {
        StringBuilder sign = new StringBuilder();
        for (byte aByte : bytes) {
            String hex = Integer.toHexString(aByte & 0xFF);
            if (hex.length() == 1) {
                sign.append("0");
            }
            sign.append(hex.toUpperCase());
        }
        return sign.toString();
    }
}
